
#include <gl/freeglut.h>
#include "Duck.h"
#include "RailGun.h"
#include "Balloon.h"
#include <cmath>
#include "LoadImg.h"
#include "Utilities.h"

//set array indexes for skybox
#define BACK_ID		0						
#define FRONT_ID	1						
#define BOTTOM_ID	2							
#define TOP_ID		3							
#define LEFT_ID		4							
#define RIGHT_ID	5	
#define GL_CLAMP_TO_EDGE 0x812F

GLuint textures[10];

int window_width = 1024;
int window_height = 768;

double aspect = double(window_width) / double(window_height);

const int period = 1000 / 60;
double time;

//initialize gamemode variables
bool wireframeMode = false;
int DisplayMode = 0;
int DUCKMODE = 0;
int GUNMODE = 1;
int BALLOONMODE = 2;
int GAMEMODE = 3;
int AUTOMODE = 4;
int CameraMode = 0;

//initialize in game objects
Duck *pMainDuck;
RailGun *pMainGun;
Balloon *pMainBalloon;
Balloon *gameBalloon1;
Balloon *gameBalloon2;
Balloon *gameBalloon3;

//beauty mode objects
Balloon *displayBalloon;
Duck *displayDuck;
RailGun *displayGun;

//initialize variables used for gun/duck alignment, velocity and collision
double xGunRot = 0;
double yGunRot = 0;
double xGunRotShot = 0;
double yGunRotShot = 0;
bool resetDuck = true;
bool duckShot = false;
bool velSet = false;
double duckVelocityX = 10;
double duckVelocityY = 10;
double duckVelocityZ = 10;
double yGravity = -10;
double shotTime = 0;
double collisionTime1 = -1;
double collisionTime2 = -1;
double collisionTime3 = -1;
bool balloon1PrevHit = true;
bool balloon2PrevHit = true;
bool balloon3PrevHit = true;

//initialize score components
int score = 0;
bool addedPoints = false;
bool shotSet = false;
double timeShotWasSet = 0.0;
double powerBar = 0.0;
int lives = 3;
bool gameOver = false;
double timeSinceBalloonsSet = 0.0;
double timer;
bool displayMultiplier = false;
double multiplierTimer;
double multiplier;
int lastBalloonPoints;

//duck lives
Duck * l1, *l2, *l3;
double currentRot = 0.0;
double balloonModeRot = 0.0;

/*
Method loads in bitmap files stored in local directory and saves them to a texture array.
The texture array is then used to form the skybox in game and auto modes.

References: Online Tutorial - http://celestialcoding.com/tutorials-23/opengl-ground-up-tutorial-16-
implementing-a-skybox/?PHPSESSID=s0sr3nhfvnp1amrc9bifpl8hq6
*/
bool LoadImage(char* file, GLuint tex[], GLuint id){
	ImageBMP img;
	if(!img.LoadBMP(file))
		return false;

	glGenTextures(1, &tex[id]);
	glBindTexture(GL_TEXTURE_2D, tex[id]);

	glTexImage2D(	GL_TEXTURE_2D,		
		0,				
		img.GetType(),		
		img.GetWidth(),			
		img.GetHeight(),		
		0,						
		GL_BGR_EXT,				
		GL_UNSIGNED_BYTE,		
		img.GetImageData(0));

	gluBuild2DMipmaps(	GL_TEXTURE_2D,
		img.GetType(),
		img.GetWidth(),
		img.GetHeight(),
		GL_BGR_EXT,			
		GL_UNSIGNED_BYTE,
		img.GetImageData(0));

	glTexParameteri(GL_TEXTURE_2D,GL_TEXTURE_MIN_FILTER,GL_LINEAR_MIPMAP_NEAREST);
	glTexParameteri(GL_TEXTURE_2D,GL_TEXTURE_MAG_FILTER,GL_LINEAR);

	/*
	The functions below cause the image to be stretched properly to cover the border lines preventing
	black lines from showing. Try commenting these out to see the effect.
	*/
	glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE);
	glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE);

	return true;
}

/*
Method creates the illusion of a skybox around the user in GAMEMODE and AUTOMODE. This method wraps the images in the
texture array around the user in a box formation, making there appear to be a vast cloud landscape.

References: Online tutorial - http://celestialcoding.com/tutorials-23/opengl-ground-up-tutorial-16-implementing-a-skybox/?PHPSESSID=s0sr3nhfvnp1amrc9bifpl8hq6
Skybox images found at: http://www.google.com/imgres?imgurl=http://www.keithlantz.net/wp-content/uploads/2011/10/skybox_texture.jpg&imgrefurl=http:
//www.keithlantz.net/2011/10/rendering-a-skybox-using-a-cube-map-with-opengl-and-glsl/&h=1536&w=2048&sz=532&tbnid=NtRjE-TdAhqyAM:&tbnh=92&tbnw=123
&prev=/search%3Fq%3Dskybox%2Bimages%26tbm%3Disch%26tbo%3Du&zoom=1&q=skybox+images&usg=__G_70fPvXSk0agT4Lf-W4AUyq13I=&docid=4Ffx4I9IwZZeaM&sa=X&ei=
IHmYUJizFKeW2AWk7ICwAg&ved=0CDEQ9QEwBQ&dur=2867
*/

void CreateSkyBox(float x, float y, float z, float width, float height, float length)
{
	glPushMatrix();

	// Enable/Disable features
	glPushAttrib(GL_ENABLE_BIT);
	glEnable(GL_TEXTURE_2D);
	glDisable(GL_DEPTH_TEST);
	glDisable(GL_LIGHTING);
	glDisable(GL_BLEND);

	//Set all vertices to white as a fallback
	glColor4f(1,1,1,1);
	glBindTexture(GL_TEXTURE_2D, textures[BACK_ID]);

	// Center skybox
	x = x - width  / 2;
	y = y - height / 2;
	z = z - length / 2;

	//Bind each image accordingly
	//FRONT_ID and other constants are explicitly set to specific array indexes beforehand
	glBegin(GL_QUADS);		
	glTexCoord2f(1.0f, 0.0f); glVertex3f(x + width, y,			z);
	glTexCoord2f(1.0f, 1.0f); glVertex3f(x + width, y + height, z); 
	glTexCoord2f(0.0f, 1.0f); glVertex3f(x,			y + height, z);
	glTexCoord2f(0.0f, 0.0f); glVertex3f(x,			y,			z);
	glEnd();

	glBindTexture(GL_TEXTURE_2D, textures[FRONT_ID]);
	glBegin(GL_QUADS);	
	glTexCoord2f(1.0f, 0.0f); glVertex3f(x,			y,			z + length);
	glTexCoord2f(1.0f, 1.0f); glVertex3f(x,			y + height, z + length);
	glTexCoord2f(0.0f, 1.0f); glVertex3f(x + width, y + height, z + length); 
	glTexCoord2f(0.0f, 0.0f); glVertex3f(x + width, y,			z + length);
	glEnd();

	glBindTexture(GL_TEXTURE_2D, textures[BOTTOM_ID]);
	glBegin(GL_QUADS);		

	glTexCoord2f(1.0f, 0.0f); glVertex3f(x,			y,			z);
	glTexCoord2f(1.0f, 1.0f); glVertex3f(x,			y,			z + length);
	glTexCoord2f(0.0f, 1.0f); glVertex3f(x + width, y,			z + length); 
	glTexCoord2f(0.0f, 0.0f); glVertex3f(x + width, y,			z);
	glEnd();
	glBindTexture(GL_TEXTURE_2D, textures[TOP_ID]);
	glBegin(GL_QUADS);		
	glTexCoord2f(0.0f, 0.0f); glVertex3f(x + width, y + height, z);
	glTexCoord2f(1.0f, 0.0f); glVertex3f(x + width, y + height, z + length); 
	glTexCoord2f(1.0f, 1.0f); glVertex3f(x,			y + height,	z + length);
	glTexCoord2f(0.0f, 1.0f); glVertex3f(x,			y + height,	z);
	glEnd();
	glBindTexture(GL_TEXTURE_2D, textures[LEFT_ID]);
	glBegin(GL_QUADS);		
	glTexCoord2f(1.0f, 1.0f); glVertex3f(x,			y + height,	z);	
	glTexCoord2f(0.0f, 1.0f); glVertex3f(x,			y + height,	z + length); 
	glTexCoord2f(0.0f, 0.0f); glVertex3f(x,			y,			z + length);
	glTexCoord2f(1.0f, 0.0f); glVertex3f(x,			y,			z);		

	glEnd();
	glBindTexture(GL_TEXTURE_2D, textures[RIGHT_ID]);
	glBegin(GL_QUADS);	
	glTexCoord2f(0.0f, 0.0f); glVertex3f(x + width, y,			z);
	glTexCoord2f(1.0f, 0.0f); glVertex3f(x + width, y,			z + length);
	glTexCoord2f(1.0f, 1.0f); glVertex3f(x + width, y + height,	z + length); 
	glTexCoord2f(0.0f, 1.0f); glVertex3f(x + width, y + height,	z);
	glEnd();

	glPopAttrib();
	glPopMatrix();
}

/*
This method sets the velocities of x, y and z which correspond to the velocities the duck is currently traveling at
X is determined by computing the sin of the y rotation of the gun, y is computed by sin of the x rotation of the gun.
Z is determined by 400(20 * 20) * x^2 - y^2
Pointers to the velocities are returned.
*/
void setVelocities(double * xVel, double * yVel, double * zVel, double power)
{
	double z, x, y;
	double pi = 3.1416;
	power = (power + 5)/10;
	x = 20 * (-sin(2*pi*yGunRotShot/360));
	y = 20 * sin(2*pi*xGunRotShot/360);
	*xVel = x*power;
	*yVel = y*power;
	//z = sqrt(abs(x)*abs(y));
	//z = sqrt((400) - (x * x) - (y * y) + (abs(x)*abs(y)/20));
	z = sqrt((400) - (x * x) - (y * y));
	*zVel = z*power;
}
/*
This method calculates if and when the duck will hit a balloon and returns the double value represented the time till collision.
MIKHAIL WRITE THIS DESCRIPTION
*/
double getCollisionTime(double xVel, double yVel, double zVel, Balloon *pTargetBalloon, Duck *pDuck)
{
	double balloonX = pTargetBalloon->getXLocation();
	double balloonY = pTargetBalloon->getYLocation() + 1.1;
	double balloonZ = pTargetBalloon->getZLocation();
	bool willHitX = false;
	bool willHitY = false;
	double zHit = 0;
	double zHitM = 0;
	double origZ = 0;
	double startZ = getStartingZ(xGunRotShot, yGunRotShot);
	double startX = pDuck->getCollisionXLocation();
	double startY = pDuck->getCollisionYLocation();
	bool passedBalloon = false;

	for(double time = 0; time < 2; time += .001)
	{
		startX += xVel * .001;
		startY += yVel * .001;
		startZ += -zVel * .001;
		origZ += -zVel * .001;
		if(origZ < -6)
			yVel += .001 * yGravity;

		//Add balloonZ with circle eq
		if(startZ <= balloonZ + 1 && startZ >= balloonZ)
		{
			passedBalloon = true;

			//Change this constant decimal to add duck rotation
			if((startX + .25 >= balloonX - 1 && startX + .25 <= balloonX + 1) ||
				(startX - .25 >= balloonX - 1 && startX - .25 <= balloonX + 1.1))
			{
				if((startY + .2 >= balloonY - 1.1 && startY + .2 <= balloonY + 1.1) ||
					(startY - .2 >= balloonY - 1.1 && startY - .2 <= balloonY + 1.1))
				{
					return time;
				}
			}
		}
		else
		{
			if(passedBalloon)
				break;
		}
	}
	return -1;
}

/* 
This method keeps track of the coordinates of the mouse with respect to the size of the window.
Every time the cursor is moved, this function is called in the main and updates the class variables xGunRot and yGunRot 
(Z's rotation will always be 0). X and y are later used to determine the placement of the gun and duck with respect to 
the rotation determined by the cursor's position. The max angle allowed is 45 degrees.
*/
void PassiveMotionFunc(int x, int y)
{
	//We only care about the cursor if we are currently under GAMEMODE
	if(DisplayMode == GAMEMODE){
		//Making sure to incorporate screen size
		xGunRot = (window_height-double(y))/window_height*40;
		yGunRot = -((-window_width/2) + double(x))/(window_width/2)*40;
	}

	pMainGun->setXDegrees(xGunRot);
	pMainGun->setYDegrees(yGunRot);
	pMainDuck->RotateDuck(xGunRot,yGunRot,0);
	glutPostRedisplay();
}

/*
This function displays all components of the program and is called from glutDisplayFunc().
The game is divided into 3 beauty modes, a game play mode and an automation mode. The 
current display mode DisplayMode is set in KeyboardFunc(). The games rules and specifications
were set via online document made by Perry Kivolowitz.
*/
void DisplayFunc()
{	
	//Duck Beauty Mode
	//rotates the duck by getting its current coordinates and then utilizes them with gluLookAt
	if(DisplayMode == DUCKMODE)
	{
		double elapsed_time = double(glutGet(GLUT_ELAPSED_TIME)) / 1000.0;
		double x = displayDuck->getXLocation();
		double y = displayDuck->getYLocation();
		double z = displayDuck->getZLocation();

		//setup projection and light
		glClearColor(0, 0, 0, 0);
		glMatrixMode(GL_PROJECTION);
		GLfloat	light_position[] = { (float)(x+2) , (float)(y+2.0) , (float)(z) , 2.0 };
		glPolygonMode(GL_FRONT_AND_BACK, wireframeMode ? GL_LINE : GL_FILL);
		glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
		glLoadIdentity();
		glLightfv(GL_LIGHT0 , GL_POSITION , light_position);
		gluPerspective(80, aspect, 1, 10);
		glViewport(0, 0, window_width, window_height);
		glMatrixMode(GL_MODELVIEW);
		glLoadIdentity();

		//accomplishes rotation
		gluLookAt(displayDuck->getXLocation(), displayDuck->getYLocation(), displayDuck->getZLocation()+2, displayDuck->getXLocation(), displayDuck->getYLocation(), displayDuck->getZLocation(), 0 , 1, 0);

		displayDuck->SetDuckLocation(0,0,0);
		glRotated(elapsed_time*60, 0, 1, 0);
		displayDuck->DisplayDuckAsLife(window_width, window_height);

		glutSwapBuffers();
	}

	//Rail Gun Beauty Mode
	//rotation is accomplished by sending elapsed_time multiplied by a constant into the display method of RailGun.
	//inside the display method, the railgun is rotated accordingly.
	if(DisplayMode == GUNMODE)
	{
		//set up projection and light
		double elapsed_time = double(glutGet(GLUT_ELAPSED_TIME)) / 1000.0;
		glClearColor(0, 0, 0, 0);
		glMatrixMode(GL_PROJECTION);
		static	GLfloat	light_position[] = { 0.0 , 4.0 , 4.0 , 1.0 };

		glPolygonMode(GL_FRONT_AND_BACK, wireframeMode ? GL_LINE : GL_FILL);
		glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
		glLoadIdentity();
		gluPerspective(120, aspect, 1, 10);
		glLightfv(GL_LIGHT0 , GL_POSITION , light_position);
		glViewport(0, 0, window_width, window_height);
		glMatrixMode(GL_MODELVIEW);
		glLoadIdentity();

		gluLookAt(0,1,-1, 0,0,-2, 0,1,0);
		displayGun->DisplayRailGun(false, elapsed_time*60);

		glutSwapBuffers();
	}

	//Balloon Beauty Mode
	//Balloon's rotation is accomplished by rotating the balloon around the y axis.
	if(DisplayMode == BALLOONMODE)
	{
		double elapsed_time = double(glutGet(GLUT_ELAPSED_TIME)) / 1000.0;

		//set up projection and light
		glClearColor(0, 0, 0, 0);
		glMatrixMode(GL_PROJECTION);
		GLfloat	light_position[] = { -0.0 , 3.0 , 3.0 , 1.0 };
		glPolygonMode(GL_FRONT_AND_BACK, wireframeMode ? GL_LINE : GL_FILL);
		glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
		glLoadIdentity();
		glLightfv(GL_LIGHT0 , GL_POSITION , light_position);
		gluPerspective(80, aspect, 1, 10);
		glViewport(0, 0, window_width, window_height);
		glMatrixMode(GL_MODELVIEW);
		glLoadIdentity();

		gluLookAt(0,0,0 ,0, -1, -4, 0 , 1, 0);
		glTranslated(0, -1, -4);
		glRotated(elapsed_time*60, 0, 1, 0);
		displayBalloon->DisplayBalloon();

		glutSwapBuffers();
	}

	//Game Play Mode and Automation Mode
	//These two are grouped together to efficently recycle code. Where they differ is the ability to miss balloons.
	//In game play 3 misses results in a loss, in auto mode, the computer is granted infinite lives.
	//Both these modes allow for the ability to change camera instantaneously via setCamera()
	if(DisplayMode == GAMEMODE || DisplayMode == AUTOMODE)
	{
		//set up light and projection
		double elapsed_time = (double(glutGet(GLUT_ELAPSED_TIME)) - time);
		time = elapsed_time + time;
		elapsed_time /= 1000;
		glClearColor(0, 0, 0, 0);
		glMatrixMode(GL_PROJECTION);
		static	GLfloat	light_position[] = { 0.0 , 4.0 , 4.0 , 1.0 };

		glPolygonMode(GL_FRONT_AND_BACK, wireframeMode ? GL_LINE : GL_FILL);
		glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
		glLoadIdentity();
		gluPerspective(120, aspect, 1, 40);
		glLightfv(GL_LIGHT0 , GL_POSITION , light_position);
		glViewport(0, 0, window_width, window_height);
		setCamera(CameraMode, pMainDuck);
		glMatrixMode(GL_MODELVIEW);
		glLoadIdentity();

		//if the camera moves with the duck than the skybox moves as well, keeping the illusion of the cloud atlas alive and strong
		if(duckShot && (CameraMode == 1 || CameraMode == 2))
		{
			CreateSkyBox((float)pMainDuck->getXLocation(), (float)pMainDuck->getYLocation(), (float)pMainDuck->getZLocation(), 40, 40, 40);
		}
		else
		{
			CreateSkyBox(0, 0, 0, 40, 40, 40);
		}

		//if auto mode then the gun's rotation is randomly determined to be within the playing field.
		if(DisplayMode == AUTOMODE){
			if(!shotSet && !duckShot){
				int rX = rand() % window_width;
				int rY = rand() % window_height;
				xGunRot = (window_height-double(rY))/window_height*45;
				yGunRot = -((-window_width/2) + double(rX))/(window_width/2)*45;
				pMainGun->setXDegrees(xGunRot);
				pMainGun->setYDegrees(yGunRot);
				pMainDuck->RotateDuck(xGunRot,yGunRot,0);
				glutPostRedisplay();
				shotSet = true;
				timeShotWasSet = time;
				gameOver = false;
				lives = 3;
			}
			//after aligning the shot, the compute waits one second and fires the duck
			if(time-timeShotWasSet > 1000){
				duckShot = true;
				xGunRotShot = xGunRot;
				yGunRotShot = yGunRot;
				shotSet = false;
			}
		}
		pMainGun->DisplayRailGun(true, NULL);
		gameBalloon1->DisplayBalloon();
		gameBalloon2->DisplayBalloon();
		gameBalloon3->DisplayBalloon();
		drawHud(score,GAMEMODE,CameraMode, window_height, window_width, elapsed_time, gameOver, timer);
		if(displayMultiplier){
			if(time - multiplierTimer > 3000){
				multiplier = 1.0;
				displayMultiplier = false;
				multiplierTimer = 0.0;
			} else {
				//set to ortho projection
				glMatrixMode(GL_PROJECTION);
				glPushMatrix();
				glLoadIdentity();
				glOrtho(0, window_width, 0, window_height, -1.0f, 1.0f);
				glMatrixMode(GL_MODELVIEW);
				glPushMatrix();
				glLoadIdentity();
				glPushAttrib(GL_DEPTH_TEST);
				glDisable(GL_DEPTH_TEST);

				//print multipler and last balloon score to screen
				glColor4f(0,1,0,1);
				char timerString[3];
				char balloonPointsString[3];
				_itoa_s((int)multiplier,timerString,10);
				_itoa_s(lastBalloonPoints, balloonPointsString, 10);
				printToScreen((float)window_width/2-50,(float)window_height-20,"+");
				printToScreen((float)window_width/2-30,(float)window_height-20,timerString);
				printToScreen((float)window_width/2-10, (float)window_height-20, "x");
				printToScreen((float)window_width/2+10, (float)window_height-20, balloonPointsString);
				printToScreen((float)window_width/2+50, (float)window_height-20, "Points!");
				//reset back to perspective mode
				glPopAttrib();
				glMatrixMode(GL_PROJECTION);
				glPopMatrix();
				glMatrixMode(GL_MODELVIEW);
				glPopMatrix();
			}
		}

		//draw balloon points directly centered and above balloon
		gameBalloon1->drawPoints(GLUT_BITMAP_HELVETICA_18);
		gameBalloon2->drawPoints(GLUT_BITMAP_HELVETICA_18);
		gameBalloon3->drawPoints(GLUT_BITMAP_HELVETICA_18);

		//if the player is out of lives dont allow the game to be played until the spacebar is hit
		//screen stays here until the spacebar is hit. Only motion allowed is the rotation of the gun.
		if(gameOver){} 
		else
		{
			if(resetDuck)
			{
				//if a balloon has just been hit then recovers and sets a new random location
				if(balloon1PrevHit && balloon2PrevHit && balloon3PrevHit)
				{
					double x, y, z;
					setRandCoords(&x, &y, &z, NULL, NULL);
					gameBalloon1->recoverDeadBalloon();
					gameBalloon1->setLocation((float)x, (float)y, (float)z);
					setRandCoords(&x, &y, &z, gameBalloon1, NULL);
					gameBalloon2->recoverDeadBalloon();
					gameBalloon2->setLocation((float)x, (float)y, (float)z);
					setRandCoords(&x, &y, &z, gameBalloon1, gameBalloon2);
					gameBalloon3->recoverDeadBalloon();
					gameBalloon3->setLocation((float)x, (float)y, (float)z);
					balloon1PrevHit = false;
					balloon2PrevHit = false;
					balloon3PrevHit = false;
					timeSinceBalloonsSet = time;
				}
				pMainDuck->SetDuckLocation(0, 0, 0);
				pMainDuck->RotateDuck(xGunRot,yGunRot, 0);
				pMainDuck->DisplayDuck();
				resetDuck = false;
			}
			//once duck has been shot then compute velocities and collision time (collisionTime = -1 if no collision will occur).
			//then use velocities to determine new location of the duck, determine gravity, and set duck's location and rotation
			//(based off the gun's rotation)
			if(duckShot)
			{
				if(!velSet)
				{
					setVelocities(&duckVelocityX, &duckVelocityY, &duckVelocityZ, powerBar);
					collisionTime1 = (!balloon1PrevHit) ? getCollisionTime(duckVelocityX, duckVelocityY, duckVelocityZ, gameBalloon1, pMainDuck) : -1;
					collisionTime2 = (!balloon2PrevHit) ? getCollisionTime(duckVelocityX, duckVelocityY, duckVelocityZ, gameBalloon2, pMainDuck) : -1;
					collisionTime3 = (!balloon3PrevHit) ? getCollisionTime(duckVelocityX, duckVelocityY, duckVelocityZ, gameBalloon3, pMainDuck) : -1;
					velSet = true;
				}

				//determine the new location of the duck using past location and velocities
				float X = (float)(pMainDuck->getXLocation()) + (float)(elapsed_time * duckVelocityX);
				float Y = (float)(pMainDuck->getYLocation()) + (float)(elapsed_time * duckVelocityY);
				float Z = (float)(pMainDuck->getZLocation()) - (float)(elapsed_time * duckVelocityZ);
				pMainDuck->SetDuckLocation(X,Y,Z);

				//don't apply gravity until the duck has cleared the gun
				if(pMainDuck->getZLocation() < -6){
					duckVelocityY += elapsed_time * yGravity;
				}

				shotTime += elapsed_time;
				pMainDuck->RotateDuck(xGunRotShot, yGunRotShot, 0.0);
				//if it is time for the duck to hit a balloon then reverse duck's z axis to mimic deflection
				//adjust variables accordingly
				if(collisionTime1 >0 && shotTime > collisionTime1 ||
					collisionTime2 >0 && shotTime > collisionTime2 ||
					collisionTime3 >0 && shotTime > collisionTime3)
				{
					bool hit1 = collisionTime1 >0 && shotTime > collisionTime1;
					bool hit2 = collisionTime2 >0 && shotTime > collisionTime2;
					bool hit3 = collisionTime3 >0 && shotTime > collisionTime3;

					if(duckVelocityZ > 0)
					{
						duckVelocityZ = -duckVelocityZ/4;
					}
					if(pMainDuck->getZLocation() > -5)
					{
						duckVelocityY -= 10;
					}

					if(hit1)
					{
						gameBalloon1->initiateDeathSequence();
						balloon1PrevHit = true;
						collisionTime2 = -1;
						collisionTime3 = -1;
						if(!addedPoints){
							multiplier = timer/1000;
							lastBalloonPoints = gameBalloon1->getPoints();
							score += (int)(lastBalloonPoints * multiplier);
							multiplierTimer = time;
							displayMultiplier = true;
							addedPoints = true;
							playHitSound();
						}
					}
					else if(hit2)
					{
						gameBalloon2->initiateDeathSequence();
						balloon2PrevHit = true;
						collisionTime1 = -1;
						collisionTime3 = -1;
						if(!addedPoints){
							multiplier = timer/1000;
							lastBalloonPoints = gameBalloon2->getPoints();
							score += (int)(lastBalloonPoints * multiplier);
							multiplierTimer = time;
							displayMultiplier = true;
							addedPoints = true;
							playHitSound();
						}
					}
					else if(hit3)
					{
						gameBalloon3->initiateDeathSequence();
						balloon3PrevHit = true;
						collisionTime2 = -1;
						collisionTime1 = -1;
						if(!addedPoints){
							multiplier = timer/1000;
							lastBalloonPoints = gameBalloon3->getPoints();
							score += (int)(lastBalloonPoints * multiplier);
							multiplierTimer = time;
							displayMultiplier = true;
							addedPoints = true;
							playHitSound();
						}
					}					
				}
				else
				{
					pMainDuck->RotateDuck(xGunRotShot, yGunRotShot, 0.0);
				}
			}
			//if duck not shot then check if time for balloons has expired (10 seconds to hit 3)
			else {
				timer = time - timeSinceBalloonsSet;
				//printToScreen(0, window_height/2, timer);
				if(timer > 10000){
					//reset balloons
					double x, y, z;
					setRandCoords(&x, &y, &z, NULL, NULL);
					gameBalloon1->recoverDeadBalloon();
					gameBalloon1->setLocation((float)x, (float)y, (float)z);
					setRandCoords(&x, &y, &z, gameBalloon1, NULL);
					gameBalloon2->recoverDeadBalloon();
					gameBalloon2->setLocation((float)x, (float)y, (float)z);
					setRandCoords(&x, &y, &z, gameBalloon1, gameBalloon2);
					gameBalloon3->recoverDeadBalloon();
					gameBalloon3->setLocation((float)x, (float)y, (float)z);
					balloon1PrevHit = false;
					balloon2PrevHit = false;
					balloon3PrevHit = false;
					timeSinceBalloonsSet = time;
				}
			}

			//if the time since shot (shotTime) ever gets greater than 3 than the duck
			if(shotTime > 3) 
			{
				resetDuck = true;
				duckShot = false;
				shotTime = 0;
				velSet = false;
				addedPoints = false;
				bool noneHit = collisionTime1 < 0 && collisionTime2 < 0 && collisionTime3 < 0;
				if(noneHit && DisplayMode != AUTOMODE){
					if(lives > 1){
						lives --;
					} else {
						gameOver = true;
					}
				}
			}
			pMainDuck->DisplayDuck();

			if(duckShot)
			{
				powerBar = drawPowerBar(elapsed_time, window_width, window_height, powerBar, true);
			}
			else
			{
				//powerBar switches to orthogonal projection
				//the rest of this if statement remains orthogonal, it gets reset on the next iteration
				powerBar = drawPowerBar(elapsed_time, window_width, window_height, powerBar, false);
			}

			//draw lives as rotating ducks in right corner
			glPushMatrix();
			if(lives > 0){
				glLoadIdentity();
				glTranslatef(4,-2.5,-2);
				glScalef((GLfloat).42,(GLfloat).42,(GLfloat).42);
				currentRot += elapsed_time*20;
				glRotated(currentRot, 0, 1, 0);
				l1->DisplayDuckAsLife(window_width, window_height);
			}
			if(lives > 1){
				glLoadIdentity();
				glTranslatef(3,-2.5,-2);
				glScalef((GLfloat).46,(GLfloat).46,(GLfloat).46);
				currentRot += elapsed_time*20;
				glRotated(currentRot, 0, 1, 0);
				l2->DisplayDuckAsLife(window_width, window_height);
			}
			if(lives > 2){
				glLoadIdentity();
				glTranslatef(2,-2.5,-2);
				glScalef((GLfloat).5,(GLfloat).5,(GLfloat).5);
				currentRot += elapsed_time*20;
				glRotated(currentRot, 0, 1, 0);
				l3->DisplayDuckAsLife(window_width, window_height);
			}
			glPopMatrix();
		}
		glutSwapBuffers();
	}
}

// This function is called when the size of the window changes.
void ReshapeFunc(int w, int h)
{
	window_height = h;
	window_width = w;
	aspect = double(w) / double(h);
	glutPostRedisplay();
}

// This function is called when an "ordinary" key is pressed.
void KeyboardFunc(unsigned char c, int x, int y)
{
	switch (c)
	{
		// Hitting lower case x or the escape key will  exit the
		// glut event loop.
	case 112:
		DisplayMode = (DisplayMode == 4) ? 0 : DisplayMode + 1;
		break;
	case 'w':
		wireframeMode = !wireframeMode;
		break;
	case ' ':
		if(gameOver){
			lives = 3;
			gameOver = false;
			score = 0;
		} else {
			if(DisplayMode == GAMEMODE){
				duckShot = true;
				xGunRotShot = xGunRot;
				yGunRotShot = yGunRot;
			}
		}
		break;
	case 'c':
		CameraMode = (CameraMode == 3) ? 0 : CameraMode + 1;
		break;
	case 'x':
	case 27:
		glutLeaveMainLoop();
		return;
	}

	glutPostRedisplay();
}

// This function is called when an "ordinary" key is pressed.
void SpecialKeyFunc(int c, int x, int y)
{
	switch (c)
	{
		// Hitting lower case x or the escape key will  exit the
		// glut event loop.
	case GLUT_KEY_F1:
		DisplayMode = (DisplayMode == 4) ? 0 : DisplayMode + 1;
		break;
	}

	glutPostRedisplay();
}

void TimerFunc(int value)
{
	glutTimerFunc(period, TimerFunc, value);
	glutPostRedisplay();
}

/* 
This function runs the program
*/
int main(int argc, char * argv[])
{
	glutInit(&argc, argv);
	glutInitWindowPosition(0, 0);
	glutInitWindowSize(window_width, window_height);
	glutInitDisplayMode(GLUT_RGBA | GLUT_DOUBLE | GLUT_DEPTH);
	glutCreateWindow("Duck Game");
	time = double(glutGet(GLUT_ELAPSED_TIME));

	//initialize objects
	//gamemode and automode objects
	double x, y, z;
	setRandCoords(&x, &y, &z, NULL, NULL);
	pMainGun = new RailGun();
	pMainDuck = new Duck();
	gameBalloon1 = new Balloon(10, 10, 10, (float)x, (float)y, (float)z);
	setRandCoords(&x, &y, &z, gameBalloon1, NULL);
	gameBalloon2 = new Balloon(10, 10, 10, (float)x, (float)y, (float)z);
	setRandCoords(&x, &y, &z, gameBalloon1, gameBalloon2);
	gameBalloon3 = new Balloon(10, 10, 10, (float)x, (float)y, (float)z);
	//beauty mode objects
	displayBalloon = new Balloon(20, 10, 80);
	displayDuck = new Duck();
	displayGun = new RailGun();
	//ducks as lives
	l1 = new Duck();
	l2 = new Duck();
	l3 = new Duck();

	//setup accepting input functions and display function
	glutDisplayFunc(DisplayFunc);
	glutReshapeFunc(ReshapeFunc);
	glutKeyboardFunc(KeyboardFunc);
	glutSpecialFunc(SpecialKeyFunc);
	glutPassiveMotionFunc(PassiveMotionFunc);
	glutTimerFunc(period, TimerFunc, 0);

	glClearColor(0.0 , 0.0 , 0.0 , 0.0);
	glEnable(GL_DEPTH_TEST);
	glEnable(GL_COLOR_MATERIAL);
	glEnable(GL_LIGHTING); //Enable lighting
	glEnable(GL_LIGHT0); //Enable light #0
	//glEnable(GL_LIGHT1); //Enable light #1
	glEnable(GL_NORMALIZE); //Automatically normalize normals
	glShadeModel(GL_SMOOTH); //Enable smooth shading

	//load all the images used for the skybox and assign each their appropriate position in the texture array
	//if failure on any image main returns -1 and program quits
	if(	!LoadImage("Top2.bmp", textures, 3) ||
		!LoadImage("Bottom2.bmp", textures, 2) ||
		!LoadImage("Left2.bmp", textures, 4) ||
		!LoadImage("Right2.bmp", textures, 5) ||
		!LoadImage("Front2.bmp", textures, 0) ||
		!LoadImage("Back2.bmp", textures, 1)){
			return -1;
	}
	glutMainLoop();
	return 0;
}
